現在畫面上還未處理的區塊就剩下未來一星期的天氣還沒做了,這篇文章將會來示範如何處理這個區塊。
在 Day 24 的文章中有提到,按照 OpenWeatherMap 的文件指示,要取得每日天氣的話,可以使用來自 daily
的資料,這是一個陣列,裡面包含了未來一星期的天氣預報,而每個物件都包含了一天的天氣資料,其中包含了最高溫、最低溫、天氣狀態等等。
所以我們只要確保 API 的 exclude
參數不包含 daily
,就可以取得未來一星期的天氣預報了,就像這樣:
https://api.openweathermap.org/data/3.0/onecall?lat=25.042435&lon=121.513878&exclude=minutely,alert&units=metric&appid={APIKEI}
型別的部分就像這樣:
interface WeatherDetail {
description: string
icon: string
id: number
main: string
}
export interface DailyWeatherInfo {
dt: number
temp: {
max: number
min: number
}
weather: WeatherDetail[],
readableDay: string
}
interface Store {
dailyWeather: DailyWeatherInfo[] | null
}
跟 HourlyWeatherInfo
很像,差別在於 temp
的部分,因為 daily
的資料中,temp
是一個物件,裡面包含了 max
跟 min
,分別代表當天的最高溫跟最低溫。
然後 Store 的部分:
const convertDailyToReadableDay = (dailyData: DailyWeatherInfo[]) => {
return dailyData.map((day: DailyWeatherInfo) => {
const readableDay = dayjs.unix(day.dt).format('dddd')
return { ...day, readableDay }
})
}
// 省略其他程式碼
const useStore = create<Store>((set) => ({
// 省略其他程式碼
dailyWeather: null,
fetchWeatherData: async () => {
const { location, errorMsg } = useStore.getState()
if (location && location.coords) {
const { latitude, longitude } = location.coords
const apiUrl = process.env.EXPO_PUBLIC_WEATHER_API_URL
const apiKey = process.env.EXPO_PUBLIC_WEATHER_API_KEY
try {
const response = await fetch(
`${apiUrl}lat=${latitude}&lon=${longitude}&exclude=minutely,alert&units=metric&appid=${apiKey}`
)
const json = await response.json()
set({
currentWeather: json.current,
hourlyWeather: convertHourlyToReadableTime(json.hourly),
dailyWeather: convertDailyToReadableDay(
json.daily.map((day: DailyWeatherInfo) => ({
dt: day.dt,
temp: {
max: day.temp.max,
min: day.temp.min
},
weather: day.weather
}))
)
})
} catch (err) {
console.log(err)
}
}
},
}))
需要一個 convertDailyToReadableDay()
來將時間戳記轉換成可讀的時間,然後在 fetchWeatherData
中,將 daily
的資料轉換成我們需要的格式。
接著到 App.tsx
中,將 dailyWeather
的資料取出來,並且使用 FlatList
來展示:
import { useEffect, memo } from 'react'
import {
View,
SafeAreaView,
FlatList,
useWindowDimensions
} from 'react-native'
import { StatusBar } from 'expo-status-bar'
import * as Location from 'expo-location'
import useStore from './src/store'
import {
CurrentWeather,
DailyForecast,
HourlyForecast,
HeaderInfo
} from './src/components'
import { DailyWeatherInfo } from './src/store'
export default function App() {
const {
location,
errorMsg,
setLocation,
setErrorMsg,
fetchWeatherData,
fetchLocation,
hourlyWeather,
dailyWeather
} = useStore()
// 省略其他程式碼
const renderDailyItem = ({ item }: { item: DailyWeatherInfo }) => {
const { temp, readableDay } = item
const tempHigh = Math.round(temp.max).toString()
const tempLow = Math.round(temp.min).toString()
return (
<DailyForecast
day={readableDay}
icon='sunny'
tempHigh={tempHigh}
tempLow={tempLow}
/>
)
}
return (
<SafeAreaView className='flex-1 bg-blue-500'>
// 省略其他程式碼
<View className='p-6'>
<FlatList
data={dailyWeather}
keyExtractor={({ dt }) => dt.toString()}
renderItem={renderDailyItem}
/>
</View>
</SafeAreaView>
)
}
這裡使用 FlatList
來展示未來一星期的天氣預報,並且使用 renderDailyItem()
來處理每個項目的畫面。
畫面的部分就像這樣:
不過注意到又有時間上的問題,就是現在時間是 Tuesday,但是未來一星期又出現 Tuesday,這是因為 OpenWeatherMap API 中的日期是一次給 7 天的資料,所以我們需要在 convertDailyToReadableDay()
中處理這個問題:
const convertDailyToReadableDay = (
dailyData: DailyWeatherInfo[],
sliceCount: number = 7
) => {
return dailyData.slice(1, sliceCount).map((day: DailyWeatherInfo) => {
const readableDay = dayjs.unix(day.dt).format('dddd')
return { ...day, readableDay }
})
}
這樣就可以顯示不包含今天的未來一星期的天氣預報了: